package com.vmj.heap;

import com.vmj.classfile.entity.constantinfo.ConstantMethodRefInfo;

import java.util.List;

/**
 * Created by yangshoujun on 2017/8/26 下午2:43.
 */
public class MethodRef extends MemberRef {

    private Method method;

    public static MethodRef parseMethodRef(RuntimeConstantPool runtimeConstantPool, ConstantMethodRefInfo refInfo) {

        MethodRef ref = new MethodRef();
        ref.setRuntimeConstantPool(runtimeConstantPool);
        ref.copyMemberRefInfo(refInfo);

        return ref;
    }

    public Method resolvedMethod() {
        if(method == null) {
            resolvedMethodRef();
        }
        return method;
    }

    public void resolvedMethodRef() {
        VMClass thisClass = this.getRuntimeConstantPool().getClazz();
        VMClass otherClass = resolvedClass();
        if(otherClass.isInterface()) {
            throw new RuntimeException("java.lang.IncompatibalClassChangeError");
        }

        Method method = lookupMethod(otherClass, this.getName(), this.getDescriptor());
        if(method == null) {
            throw new RuntimeException("java.lang.NoSuchMethodError");
        }
        if(!method.isAccessibleTo(thisClass)) {
            throw new RuntimeException("java.lang.IllegalAccessError");
        }
        this.method = method;
    }

    private Method lookupMethod(VMClass clazz, String name, String descriptor) {
        Method method = lookupMethodInClass(clazz, this.getName(), this.getDescriptor());
        if(method == null) {
            method = lookupMethodInInterfaces(clazz.getInterfaces(), this.getName(), this.getDescriptor());
        }
        return method;
    }

    public static Method lookupMethodInClass(VMClass clazz, String name, String descriptor) {
        for(VMClass cls = clazz; cls != null; cls = cls.getSuperClass()) {
            for(Method method : cls.getMethods()) {
                if(method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
                    return method;
                }
            }
        }
        return null;
    }

    private Method lookupMethodInInterfaces(List<VMClass> ifaces, String name, String descriptor) {
        for(VMClass itf : ifaces) {
            for(Method method : itf.getMethods()) {
                if(method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
                    return method;
                }
            }
            return lookupMethodInInterfaces(itf.getInterfaces(), name, descriptor);
        }
        return null;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return method.toString();
    }
}
